package genericReflection;

import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.util.Arrays;
import java.util.Scanner;

public class GenericReflectionTest {
    public static void main(String[] args) {
        String name;
        if (args.length > 0) {
            name = args[0];
        } else {
            try (var in = new Scanner(System.in)) {
                System.out.println("Enter class name (e.g. java.util.Collections): ");
                name = in.next();
            }
        }

        try {
            Class<?> cl = Class.forName(name);

            printClass(cl);

            for (Method m : cl.getMethods()) {
                System.out.println(m);
            }
        }
        catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    public static void printClass(Class<?> cl) {
        // class java.util.ArrayList
        System.out.print(cl);

        // <E>   => TypeVariable
        printTypes(cl.getTypeParameters(), "<", ", ", ">", true);

        // extends java.util.AbstractList<E>  => ParameterizedType
        Type sc = cl.getGenericSuperclass();
        if (sc != null) {
            System.out.print("\n\textends ");
            printType(sc, false);
        }

        //  implements java.util.List<E>, java.util.RandomAccess, java.lang.Cloneable, java.io.Serializable
        printTypes(cl.getGenericInterfaces(), "\n\timplements ", ", ", "", false);
        System.out.println("\n");
    }

    public static void printTypes(Type[] types, String pre, String sep, String suf, boolean isDefinition) {
        if (pre.equals(" extends ") && Arrays.equals(types, new Type[] {Object.class})) {
            return;
        }

        // 打印前缀
        if (types.length > 0) {
            System.out.print(pre);
        }

        for (int i = 0; i < types.length; i++) {
            if (i > 0) {
                // 打印分隔符
                System.out.print(sep);
            }
            printType(types[i], isDefinition);
        }

        // 打印后缀
        if (types.length > 0) {
            System.out.print(suf);
        }
    }

    @SuppressWarnings("rawtypes")
    public static void printType(Type type, boolean isDefinition) {
        switch (type) {
            case Class t -> {           // String, Integer
                System.out.print(t.getName());
            }
            case TypeVariable t -> {    // T, E, K, V
                System.out.print(t.getName());
                if (isDefinition) {
                    printTypes(t.getBounds(), " extends ", " & ", "", false);
                }
            }
            case WildcardType t -> {    // ?, ? super E, ? extends E
                System.out.print("?");
                printTypes(t.getUpperBounds(), " extends ", " & ", "", false);
                printTypes(t.getLowerBounds(), " super ", " & ", "", false);
            }
            case ParameterizedType t -> {   // List<E>
                Type owner = t.getOwnerType();
                if (owner != null) {
                    printType(owner, false);
                    System.out.print(".");
                }
                printType(t.getRawType(), false);
                printTypes(t.getActualTypeArguments(), "<", ", ", ">", false);
            }
            case GenericArrayType t -> {    // E[]
                printType(t.getGenericComponentType(), isDefinition);
                System.out.print("[]");
            }
            default -> {}
        }
    }
}

// java ch08/genericReflection/GenericReflectionTest.java java.util.ArrayList

